package jflowmap.views.heatmap;

import java.awt.Color;
import java.awt.geom.Point2D;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import jflowmap.data.ValueStatistics;
import jflowmap.model.graph.Graph;
import jflowmap.model.graph.Node;
import jflowmap.model.ontology.Dimension;
import jflowmap.model.ontology.Measurement;
import jflowmap.model.ontology.SelectionListener;
import jflowmap.model.ontology.Value;
import jflowmap.model.ontology.View;
import jflowmap.util.InformativeComparator;
import jflowmap.util.piccolo.PNodes;
import jflowmap.views.heatmap.PInfo.Position;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public class HeatMapLayer extends AbstractLayer implements SelectionListener {
	public static final Color BACKGROUND_COLOR = new Color(240, 240, 240);
	
	private final VisualHeatMap visualHeatMap;
	private final Graph graph;
	private final PInfo info;
	private final HeatMapHeader header;
	
	private final Map<Measurement, ValueStatistics> measurementStatistics = Maps.newHashMap();
	private final Map<Dimension, HeatMapRow> rows = Maps.newTreeMap();
	
	private InformativeComparator<HeatMapRow> rowComparator = HeatMapRow.COMPARE_BY_NAME;
	private double cellWidth;
	
	public HeatMapLayer(VisualHeatMap visualHeatMap, double relativeWidth) {
		super(relativeWidth);
		this.visualHeatMap = visualHeatMap;
		this.graph = visualHeatMap.getGraph();
		this.info = new PInfo(this.getCamera());
		this.header = new HeatMapHeader(this.getCamera(), visualHeatMap);
		
		for (final Measurement measurement: graph.getMeasurements()) {	
			measurementStatistics.put(measurement, ValueStatistics.createFor(graph.getValuesMeasuredBy(measurement)));
		}
		
		getCamera().setPaint(BACKGROUND_COLOR);
		getCamera().addChild(this.info);
		
		info.add("Ansicht:", "absolute Werte");
		info.set(graph.getCommonTargetDimension().get("plural", String.class) + ":", rowComparator.getDescription());
		info.set("Reihung:", "");
		
		info.setPosition(Position.BOTTOM_CENTER);
		
		
		drawRows(cellWidth = createRows());
		this.addChild(header);
		update();
	}
	
	public Graph getGraph() {
		return this.graph;
	}
	
	public HeatMapHeader getHeader() {
		return this.header;
	}
	
	public PInfo getInfo() {
		return this.info;
	}
	
	public ValueStatistics getValueStatistics (Measurement measurement) {
		return measurementStatistics.get(measurement);
	}
	
	private void drawRows(double maxCellWidth) {
		double y = 0;
		for (final Entry<Dimension, HeatMapRow> row: rows.entrySet()) {
			row.getValue().setCellWidth(maxCellWidth);
			row.getValue().update();
		}
	}

	private double createRows() {
		double maxCellWidth = 0;
		
		for (Node target: graph.getTargetNodes()) {
			final HeatMapRow row = new HeatMapRow(target, this);
			rows.put(target.getDimension(), row);
			
			getView().getSelection("standard").addSelectionListener(target.getDimension(), this);
			if (getView().getSelection("standard").isSelected("TARGET", target.getDimension())) {
				this.addChild(row);
			} 
			
			if (row.getMaxCellWidth() > maxCellWidth)
				maxCellWidth = row.getMaxCellWidth();
		}

		return maxCellWidth;
	}
	

	
	public void orderBy (final Dimension sourceDimension, final Dimension valueDimension) {
		rowComparator = new InformativeComparator<HeatMapRow>() {
			@Override
			public int compare(HeatMapRow row1, HeatMapRow row2) {
				return Value.COMPARE_SAFELY.compare(row2.get(sourceDimension, valueDimension).getValue(),
													row1.get(sourceDimension, valueDimension).getValue());
			}
			
			@Override
			public String getDescription() {
				return "geordnet nach " + sourceDimension.getName() + " aus " + valueDimension.getName();
			}
			
		};
	}
	
	public void orderByName() {
		rowComparator = HeatMapRow.COMPARE_BY_NAME;
	}
	
	public void update () {
		final List<HeatMapRow> rows = Lists.newLinkedList(PNodes.childrenOfType(this, HeatMapRow.class));
		Collections.sort(rows, rowComparator);
		
		double y = 0.0;
		for (HeatMapRow row: rows) {
			row.update();
			row.setGlobalTranslation(new Point2D.Double(0, y));
			y += row.getFullBoundsReference().height + 4;
		}
		
		header.update();
		super.update();
	}
	
	public View getView() {
		return visualHeatMap.getHeatMapView().getView();
	}
	
	public VisualHeatMap getVisualHeatMap() {
		return visualHeatMap;
	}

	@Override
	public void select(String category, Dimension dimension) {
		if ("TARGET".equals(category)) {
			this.addChild(rows.get(dimension));
		}
	}

	@Override
	public void unselect(String category, Dimension dimension) {
		if ("TARGET".equals(category)) {
			this.removeChild(rows.get(dimension));
		}
	}

	public InformativeComparator<HeatMapRow> getRowComparator() {
		return this.rowComparator;
	}

	@Override
	public void dispose() {
		for (final Node target: graph.getTargetNodes()) {
			getView().getSelection("standard").removeSelectionListener(target.getDimension(), this);
		}
		
		for (final HeatMapRow row: this.rows.values()) {
			row.dispose();
		}
	}
}



